using System;
using System.Reflection;
using System.Collections;

namespace Widgetsphere.Core.Util
{
  public partial class ReflectionHelper
  {
    #region Constructor

    private ReflectionHelper()
    {
    }

    #endregion

    #region CreateInstance

    public static Object CreateInstance(string assemblyName, string type)
    {
      try
      {
        System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(assemblyName);

        // Calls through reflection mask exceptions in a 
        // TargetInvocationException, which is annoying.
        // Un-mask by rethrowing the inner exception
        object o = assembly.CreateInstance(type, true);
        return o;
      }
      catch(TargetInvocationException e)
      {
        throw e.InnerException;
      }
      catch(Exception ex)
      {
        throw ex;
      }

    }

    public static Object CreateInstance(System.Type type, Object[] args)
    {
      try
      {
        // Calls through reflection mask exceptions in a 
        // TargetInvocationException, which is annoying.
        // Un-mask by rethrowing the inner exception
        return Activator.CreateInstance(type, args);
      }
      catch(TargetInvocationException e)
      {
        throw e.InnerException;
      }
      catch(Exception ex)
      {
        throw ex;
      }
    }

    public static Object CreateInstance(Type type)
    {
      return Activator.CreateInstance(type);
    }

    #endregion

    #region GetType

    public static Type GetType(string assemblyName, string type)
    {
      try
      {
        System.Reflection.Assembly assembly = System.Reflection.Assembly.LoadFrom(assemblyName);

        // Calls through reflection mask exceptions in a 
        // TargetInvocationException, which is annoying.
        // Un-mask by rethrowing the inner exception
        Type o = assembly.GetType(type, true);
        return o;
      }
      catch(TargetInvocationException e)
      {
        throw e.InnerException;
      }
      catch(Exception ex)
      {
        throw ex;
      }

    }

    #endregion

    #region LoadAllAssembliesForPath

    private static ArrayList LoadAllAssembliesForPath(string path)
    {
      //Store the current assembly file name
      string current = Assembly.GetExecutingAssembly().Location.ToLower();

      ArrayList assemblyList = new ArrayList();
      string[] files = System.IO.Directory.GetFiles(path, "*.dll");
      foreach(string fileName in files)
      {
        if(!fileName.ToLower().Equals(current))
        {
          //try loading this file as an assembly
          try
          {
            System.Reflection.Assembly assembly = Assembly.LoadFrom(fileName);
            assemblyList.Add(assembly);
          }
          catch(Exception ex)
          {
            //Do nothing as the file is not a valid VS.NET file
            System.Diagnostics.Trace.WriteLine(ex.ToString());
          }
        }
      }

      string executingAssemblyPath = Assembly.GetExecutingAssembly().Location;
      System.IO.FileInfo fi = new System.IO.FileInfo(executingAssemblyPath);
      executingAssemblyPath = fi.DirectoryName;
      if(StringHelper.Match(path, executingAssemblyPath, true))
      {
        assemblyList.Add(Assembly.GetExecutingAssembly());
      }
      return assemblyList;
    }

    #endregion

    #region GetObjectsFromPath
    public static System.Type[] GetCreatableObjects(System.Type parentType, string path)
    {
      ArrayList retval = new ArrayList();
      ArrayList al = LoadAllAssembliesForPath(path);

      try
      {
        foreach(System.Reflection.Assembly assembly in al)
        {
          System.Diagnostics.Debug.WriteLine(assembly.Location);
          foreach(Type t in assembly.GetTypes())
          {
            if(parentType.IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface)
              retval.Add(t);
            else if(t.IsAssignableFrom(parentType) && !t.IsAbstract && !t.IsInterface)
              retval.Add(t);
          }
        }
      }
      catch(Exception ex)
      {
        throw ex;
      }

      return (System.Type[])retval.ToArray(typeof(System.Type));
    }

    #endregion

    #region GetByAttribute

    public static Hashtable GetFieldsByAttribute(System.Type objectType, System.Type attributeType)
    {
      try
      {
        Hashtable retval = new Hashtable();
        FieldInfo[] fields = objectType.GetFields();
        foreach(FieldInfo field in fields)
        {
          object[] attributes = field.GetCustomAttributes(attributeType, true);
          if(attributes.Length > 0)
          {
            retval.Add(field, attributes[0]);
          }
        }
        return retval;
      }
      catch(Exception ex)
      {
        throw ex;
      }
    }

    public static Hashtable GetFieldsByAttribute(object o, System.Type attributeType)
    {
      try
      {
        Type objectType = o.GetType();
        Hashtable retval = new Hashtable();
        FieldInfo[] fields = objectType.GetFields();
        foreach(FieldInfo field in fields)
        {
          object[] attributes = field.GetCustomAttributes(attributeType, true);
          if(attributes.Length > 0)
          {
            retval.Add(field, attributes[0]);
          }
        }
        return retval;
      }
      catch(Exception ex)
      {
        throw ex;
      }
    }

    public static Hashtable GetPropertiesByAttribute(System.Type objectType, System.Type attributeType)
    {
      try
      {
        Hashtable retval = new Hashtable();
        PropertyInfo[] properties = objectType.GetProperties();
        foreach(PropertyInfo property in properties)
        {
          object[] attributes = property.GetCustomAttributes(attributeType, true);
          if(attributes.Length > 0)
          {
            retval.Add(property, attributes[0]);
          }
        }
        return retval;
      }
      catch(Exception ex)
      {
        throw ex;
      }
    }

    public static Hashtable GetPropertiesByAttribute(object o, System.Type attributeType)
    {
      return GetPropertiesByAttribute(o.GetType(), attributeType);
    }

    public static Hashtable GetMethodsByAttribute(System.Type parentType, System.Type attributeType)
    {
      try
      {
        Hashtable retval = new Hashtable();
        MethodInfo[] methods = parentType.GetMethods();
        foreach(MethodInfo m in methods)
        {
          object[] attributes = m.GetCustomAttributes(attributeType, true);
          if(attributes.Length > 0)
          {
            retval.Add(attributes[0], m);
          }
        }

        //Sort the array

        return retval;
      }
      catch(Exception ex)
      {
        throw ex;
      }
    }

    #endregion

    #region GetSingleAttribute

    public static Attribute GetSingleAttribute(System.Type attributeType, object instance)
    {
      try
      {
        Hashtable retval = new Hashtable();
        Attribute[] attributes = (Attribute[])instance.GetType().GetCustomAttributes(attributeType, true);
        if(attributes.Length > 0)
          return attributes[0];
        else
          return null;
      }
      catch(Exception ex)
      {
        throw ex;
      }
    }

    public static Attribute GetSingleAttribute(System.Type attributeType, System.Type type)
    {
      try
      {
        Hashtable retval = new Hashtable();
        Attribute[] attributes = (Attribute[])type.GetCustomAttributes(attributeType, true);
        if(attributes.Length > 0)
          return attributes[0];
        else
          return null;
      }
      catch(Exception ex)
      {
        throw ex;
      }
    }

    public static Attribute[] GetAttributes(System.Type attributeType, object instance)
    {
      try
      {
        Hashtable retval = new Hashtable();
        Attribute[] attributes = (Attribute[])instance.GetType().GetCustomAttributes(attributeType, true);
        return attributes;
      }
      catch(Exception ex)
      {
        throw ex;
      }
    }

    #endregion

    #region IsTypeOf

    public static bool IsTypeOf(System.Type checkType, string baseType)
    {
      if(checkType == null)
        return false;
      while((checkType != null) && (checkType.ToString() != baseType))
      {
        checkType = checkType.BaseType;
      }
      return (checkType != null);
    }

    public static bool IsTypeOf(System.Type checkType, System.Type baseType)
    {
      return IsTypeOf(checkType, baseType.ToString());
    }

    #endregion

		#region DisplayProperties

		public static void DisplayProperties(object o)
    {
      foreach(PropertyInfo info in o.GetType().GetProperties())
      {
        System.Diagnostics.Debug.WriteLine(info.Name);
      }
    }

    public static void DisplayObjectTypes(object o)
    {
      foreach(System.Type t1 in o.GetType().GetNestedTypes())
      {
        System.Diagnostics.Debug.WriteLine(t1.ToString());
      }
      foreach(System.Type t in o.GetType().GetInterfaces())
      {
        System.Diagnostics.Debug.WriteLine(t.ToString());
      }
      foreach(MethodInfo mi in o.GetType().GetMethods())
      {
        System.Diagnostics.Debug.WriteLine(mi.Name);
      }
		}

		#endregion

		#region Implements Interface
		public static bool ImplementsInterface(object o, Type interfaceType)
    {
      foreach(System.Type t in o.GetType().GetInterfaces())
      {
        if(t == interfaceType)
          return true;
      }
      return false;
    }
    public static bool ImplementsInterface(Type objectType, Type interfaceType)
    {
      foreach(System.Type t in objectType.GetInterfaces())
      {
        if(t == interfaceType)
          return true;
      }
      return false;
    }

    public static System.Type[] GetCreatableObjectImplementsInterface(Type interfaceType, Assembly assembly)
    {
      ArrayList retval = new ArrayList();
      try
      {
        foreach(Type t in assembly.GetTypes())
        {
          if(ImplementsInterface(t, interfaceType) && !t.IsAbstract && !t.IsInterface)
            retval.Add(t);
        }
      }
      catch(Exception ex)
      {
        System.Diagnostics.Trace.WriteLine("Could not load types for assembly: " + assembly.FullName);
      }
      return (System.Type[])retval.ToArray(typeof(System.Type));
    }

    public static System.Type[] GetCreatableObjectImplementsInterface(Type interfaceType, string path)
    {
      ArrayList al = new ArrayList();
      if(System.IO.File.Exists(path))
      {
        try
        {
          al.Add(Assembly.LoadFile(path));
        }
        catch(System.BadImageFormatException ex)
        {
          //Do Nothing
        }
        catch(Exception ex)
        {
          throw;
        }
      }
      else
      {
        al = LoadAllAssembliesForPath(StringHelper.EnsureDirectorySeperatorAtEnd(path));
      }

      ArrayList retval = new ArrayList();
      try
      {
        foreach(System.Reflection.Assembly assembly in al)
        {
          try
          {
            foreach(Type t in assembly.GetTypes())
            {
              if(ImplementsInterface(t, interfaceType) && !t.IsAbstract && !t.IsInterface)
                retval.Add(t);
            }
          }
          catch(System.Reflection.ReflectionTypeLoadException ex)
          {
            System.Diagnostics.Trace.WriteLine("Could not load types for assembly: " + assembly.FullName);
            foreach(Exception innerEx in ex.LoaderExceptions)
            {
              System.Diagnostics.Trace.WriteLine("Loader Exception: " + innerEx.Message);
            }
          }
          catch(Exception ex)
          {
            System.Diagnostics.Trace.WriteLine("Could not load types for assembly: " + assembly.FullName);
          }
        }
      }
      catch(Exception ex)
      {
        throw ex;
      }

      return (System.Type[])retval.ToArray(typeof(System.Type));
    }
    #endregion

  }
}